//
// Created by 文道江 on 15/11/20.
// Copyright (c) 2015 文道江. All rights reserved.
//

#include "HybridBridge.h"
@implementation AppTheme
- (instancetype)initWithPrimaryColor:(UIColor *)primaryColor color:(UIColor *)color lightStatusBar:(BOOL)lightStatusBar {
    self = [super init];
    if (self) {
        self.primaryColor = primaryColor;
        self.color = color;
        self.lightStatusBar = lightStatusBar;
    }

    return self;
}

+ (instancetype)themeWithPrimaryColor:(UIColor *)primaryColor color:(UIColor *)color lightStatusBar:(BOOL)lightStatusBar {
    return [[self alloc] initWithPrimaryColor:primaryColor color:color lightStatusBar:lightStatusBar];
}

@end

@implementation HybridBridge

+ (instancetype)defaultHybridBridge {
    static HybridBridge *_instance = nil;

    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] init];
        }
    }

    return _instance;
}

+ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate controller:(UIViewController *)controller handler:(WVJBHandler)messageHandler {
    /*static HybridBridge *_instance = nil;

    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] initWithWebView:webView webViewDelegate:webViewDelegate controller:controller handler:messageHandler];
        }
    }

    return _instance;*/
    static NSMutableDictionary *_instances = nil;
    if(_instances == nil){
        _instances = [[NSMutableDictionary alloc] initWithCapacity: 1];
    }
    NSString *clsName = [[webView.class description] stringByAppendingFormat:@"%i", webView.hash];
    @synchronized (self) {
        if (_instances[clsName] == nil) {
            _instances[clsName] = [[self alloc] initWithWebView:webView webViewDelegate:webViewDelegate controller:controller handler:messageHandler];
        }
    }

    return _instances[clsName];
}

+ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate handler:(WVJBHandler)messageHandler{
    return [self bridgeForWebView:webView webViewDelegate:webViewDelegate controller:nil handler:messageHandler];
}

- (instancetype)init {
    self = [super init];
    if (self){
        NSLog(@"%@ init ....", self.class);
        _plugins = [NSMutableDictionary dictionaryWithCapacity:5];
        HybridEventBus *eventBus = [HybridEventBus defaultEventBus];
        [eventBus subscribe:APP_EVENT_BECOME_ACTIVE UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    dispatch_async(dispatch_get_main_queue(), ^() {
                        [_plugins[key] performSelector:@selector(didBecomeActive)];
                    });
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_DID_ENTER_BACKGROUND UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    /*dispatch_async(dispatch_get_main_queue(), ^() {
                        [_plugins[key] performSelector:@selector(enterBackground)];
                    });*/
                    // Start the long-running task and return immediately.
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        [_plugins[key] performSelector:@selector(enterBackground)];
                    });
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_WILL_ENTER_FOREGROUND UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    dispatch_async(dispatch_get_main_queue(), ^() {
                        [_plugins[key] performSelector:@selector(enterForeground)];
                    });
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_WILL_RESIGN_ACTIVE UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    dispatch_async(dispatch_get_main_queue(), ^() {
                        [_plugins[key] performSelector:@selector(willResignActive)];
                    });
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_REGISTER_USER_NOTIFICATION_SETTINGS UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] didRegisterUserNotificationSettings: notification.userInfo[@"notificationSettings"]];
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_REGISTER_FOR_REMOTE_NOTIFICATIONS_WITH_DEVICE_TOKEN UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] didRegisterForRemoteNotificationsWithDeviceToken:notification.userInfo[@"deviceToken"]];
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_FAIL_TO_REGISTER_FOR_REMOTE_NOTIFICATIONS_WITH_ERROR UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] didFailToRegisterForRemoteNotificationsWithError:notification.userInfo[@"error"]];
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_RECEIVE_REMOTE_NOTIFICATION UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] didReceiveRemoteNotification:notification.userInfo[@"userInfo"] fetchCompletionHandler:notification.userInfo[@"completionHandler"]];
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_RECEIVE_LOCAL_NOTIFICATION UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] didReceiveLocalNotification:notification.userInfo[@"notification"]];
                }
            }
        }];

        [eventBus subscribe:APP_EVENT_PERFORM_FETCH_WITH_COMPLETION_HANDLER UsingBlock:^(NSNotification *notification) {
            for(NSString *key in _plugins){
                if([_plugins[key] isKindOfClass:[HybridPlugin class]]){
                    [_plugins[key] performFetchWithCompletionHandler:notification.userInfo[@"completionHandler"]];
                }
            }
        }];

        NSString *path = [[NSBundle mainBundle] pathForResource:@"AppHybridBridge" ofType:@"plist"];
        self.appInfo = [NSDictionary dictionaryWithContentsOfFile:path];

        //网络监听
        [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            NSDictionary *data = @{
                            @"status": @(status),
                            @"statusString": [[AFNetworkReachabilityManager sharedManager] localizedNetworkReachabilityStatusString]
                    };
            [self dispatchEvent:@"networkChanged" data: data];
            [[HybridEventBus defaultEventBus] postData:data Tag:@"networkChanged"];
        }];
        [[AFNetworkReachabilityManager sharedManager] startMonitoring];

        //字体图标
        [TBCityIconFont setFontName:@"hybridbridgeiconfont"];
    }
    return self;
}

- (instancetype)initWithWebView:(WVJB_WEBVIEW_TYPE *)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE *)webViewDelegate controller:(UIViewController *)controller handler:(WVJBHandler)messageHandler {
    self = [self init];
    if (self){
        self.webView = webView;
        self.controller = controller;
        self.view = controller.view;
        self.webViewClient = [WebViewClient clientWithController:controller];
        self.javascriptBridge = [self.webViewClient bridgeForWebView:webView webViewDelegate:webViewDelegate handler:^(id data, WVJBResponseCallback responseCallback) {
            [self execute:data Handler:responseCallback WVJBHandler:messageHandler];
        }];
    }
    return self;
}

- (instancetype)initWithWebView:(WVJB_WEBVIEW_TYPE *)webView webViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE *)webViewDelegate handler:(WVJBHandler)messageHandler {
    return [self initWithWebView:webView webViewDelegate:webViewDelegate controller:nil handler:messageHandler];
}

- (void)setController:(UIViewController *)controller {
    _controller = controller;
    _view = controller.view;
}

- (void)execute:(id)data Handler:(WVJBResponseCallback) responseCallback WVJBHandler:(WVJBHandler)messageHandler{
    if (data[@"handlerName"]){
        NSString *handlerName = [@"Hybrid" stringByAppendingFormat:@"%@Handler", data[@"handlerName"]];//[data[@"handlerName"] stringByAppendingString:@"Handler"];
        Class cls = NSClassFromString(handlerName);
        if (cls){
            HybridHandler *handler = [cls handlerWithWebView:_webView controller:_controller];
            handler.bridge = self;
            handler.webView = self.webView;
            [handler Run:data[@"data"] callback:^(BOOL resultStatus, id resultData) {
                if (resultData == nil){
                    resultData = [NSNull null];
                }
                if (responseCallback){
                    responseCallback(@{
                            @"status": @(resultStatus),
                            @"data": resultData
                    });
                }
            }];
        }else{
            NSLog(@"Handler '%@' not found.", handlerName);
        }
    }else{
        if (messageHandler)messageHandler(data, responseCallback);
    }
}

- (void)executeAction:(NSString *)action Params:(id)params Handler:(WVJBResponseCallback) responseCallback{
    NSArray *opt = [action componentsSeparatedByString:@"/"];
    [self execute:@{
            @"handlerName": @"Action",
            @"data": @{
                    @"action": opt[0],
                    @"method": opt[1],
                    @"params": params
            }
    } Handler:responseCallback WVJBHandler:nil];
}

- (void)dispatchEvent:(NSString *)eventName data:(id)data {
    [_javascriptBridge callHandler:@"dispatchEvent" data:@{
            @"event": eventName,
            @"data": data
    }];
}

- (void)loadPlugin:(NSString *)pluginName {
    if(![pluginName hasSuffix:@"Plugin"]){
        pluginName = [pluginName stringByAppendingString:@"Plugin"];
    }
    if(_plugins[pluginName] == nil){
        Class cls = NSClassFromString(pluginName);
        if(cls){
            HybridPlugin *handler = (HybridPlugin *) [cls handlerWithWebView:self.webView controller:self.controller];
            handler.bridge = self;
            [handler pluginInitialize];
            _plugins[pluginName] = handler;
            NSLog(@"load plugin %@ success, %@", pluginName, _plugins);
        }else{
            NSLog(@"plugin %@ not found.", pluginName);
        }
    }
}

@end


#pragma mark HybridEventBus
/**
 * HybridEventBus
 */
@implementation HybridEventBus{
    NSNotificationCenter *_notificationCenter;
    NSOperationQueue *_queue;
}

+ (HybridEventBus *)defaultEventBus {
    static HybridEventBus *_instance = nil;

    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] init];
        }
    }

    return _instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _notificationCenter = [[NSNotificationCenter alloc] init];
        _queue = [[NSOperationQueue alloc] init];
    }

    return self;
}

- (void)subscribe:(NSString *)tag UsingBlock:(EventBusHandler)handler {
    //NSLog(@"subscribe , %@", tag);
    [_notificationCenter addObserverForName:tag object:self queue:_queue usingBlock:^(NSNotification *note) {
        handler(note);
    }];
}

- (void)subscribeUsingBlock:(EventBusHandler)handler{
    [self subscribe:@"__default__" UsingBlock:handler];
}

- (void)postData:(NSDictionary *)data {
    [_notificationCenter postNotification:[NSNotification notificationWithName:@"__default__" object:self userInfo:data]];
}

- (void)postData:(NSDictionary *)data Tag:(NSString *)tag{
    //NSLog(@"post %@ , %@", tag, data);
    [_notificationCenter postNotificationName:tag object:self userInfo: data];
}

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject {
    [_notificationCenter addObserver:observer selector:aSelector name:aName object:anObject];
}

- (void)removeObserver:(id)observer {
    [_notificationCenter removeObserver:observer];
}
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject{
    [_notificationCenter removeObserver:observer name:aName object:anObject];
}

@end

#pragma mark HybridEventBus end


#pragma mark HybridHandler
/**
 * HybridHandler
 */
@implementation HybridHandler

+ (instancetype)handlerWithWebView:(WVJB_WEBVIEW_TYPE *)webView controller:(UIViewController *)controller {
    /*static HybridHandler *_instance = nil;

    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] initWithWebView:webView controller:controller];
        }
    }

    return _instance;*/
    static NSMutableDictionary *_instances = nil;
    if(_instances == nil){
        _instances = [[NSMutableDictionary alloc] initWithCapacity:5];
    }
    NSString *clsName = [[webView.class description] stringByAppendingFormat:@"%i_%@", webView.hash, [[self class] description]];
    @synchronized (self) {
        if (_instances[clsName] == nil) {
            _instances[clsName] = [[self alloc] initWithWebView:webView controller:controller];
        }
    }

    return _instances[clsName];
}

- (instancetype)initWithWebView:(WVJB_WEBVIEW_TYPE *)webView controller:(UIViewController *)controller {
    self = [self init];
    if (self){
        self.webView = webView;
        self.controller = controller;
        self.view = controller.view;
    }
    return self;
}

- (void)Run:(id)data callback:(ResultHandler)callback {
    NSLog(@"call %@/Run", self.class);
}


- (instancetype)init {
    self = [super init];
    NSLog(@"%@ init ....", self.class);
    return self;
}

- (void)send:(id)data {
    [_bridge.javascriptBridge send:data];
}

- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_bridge.javascriptBridge send:data responseCallback:responseCallback];
}

- (void)callHandler:(NSString *)handlerName {
    [self callHandler:handlerName data:nil responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data {
    [self callHandler:handlerName data:data responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_bridge.javascriptBridge callHandler:handlerName data:data responseCallback:responseCallback];
    //[_bridge execute:data Handler:responseCallback WVJBHandler:nil];
}

- (void)dispatchEvent:(NSString *)eventName data:(id)data {
    [_bridge.javascriptBridge callHandler:@"dispatchEvent" data:@{
            @"event": eventName,
            @"data": data
    }];
}

- (void)reset {
    if ([_bridge.javascriptBridge respondsToSelector:@selector(reset)]){
        [_bridge.javascriptBridge performSelector:@selector(reset)];
    }
}


@end
#pragma mark HybridHandler end



#pragma mark HybridAction
/**
 * HybridAction
 */
@implementation HybridAction

+ (instancetype)actionWithWebView:(WVJB_WEBVIEW_TYPE *)webView controller:(UIViewController *)controller {
    static NSMutableDictionary *_instances = nil;
    if(_instances == nil){
        _instances = [[NSMutableDictionary alloc] initWithCapacity:5];
    }
    NSString *clsName = [[webView.class description] stringByAppendingFormat:@"%i_%@", webView.hash, [[self class] description]];
    @synchronized (self) {
        if (_instances[clsName] == nil) {
            _instances[clsName] = [[self alloc] initWithWebView:webView controller:controller];
        }
    }

    return _instances[clsName];
}

- (instancetype)initWithWebView:(WVJB_WEBVIEW_TYPE *)webView controller:(UIViewController *)controller {
    self = [self init];
    if (self){
        self.webView = webView;
        self.controller = controller;
        self.view = controller.view;
    }
    return self;
}

- (void)actionInitialize{

}

- (void)callback:(BOOL)status data:(id)data {
    if (self.resultHandler != nil){
        self.resultHandler(status, data);
        self.resultHandler = nil;
    }
}

- (void)dispatchEvent:(NSString *)eventName data:(id)data {
    [self.hybridHandler dispatchEvent:eventName data:data];
}

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [self.hybridHandler callHandler:handlerName data:data responseCallback:responseCallback];
}

- (instancetype)init {
    self = [super init];
    NSLog(@"%@ init ....", self.class);
    return self;
}

@end
#pragma mark HybridAction end


#pragma mark HybridPlugin
/**
 * HybridPlugin
 */
@implementation HybridPlugin

- (void)pluginInitialize{
    NSLog(@"%@ - (void)pluginInitialize; ", self.class);
}

- (void)enterBackground {

}

- (void)enterForeground {

}

- (void)didBecomeActive {
    NSLog(@"%@ didBecomeActive", self.class);
}

- (void)willResignActive {

}

- (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {

}

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

}

- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

}

- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {

}

- (void)didReceiveLocalNotification:(UILocalNotification *)notification {

}

- (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {

}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"plugin method %@/%@ not found.", self.class, NSStringFromSelector(anInvocation.selector));
}

@end
#pragma mark HybridPlugin end


#pragma mark HybridActionHandler
/**
 * HybridActionHandler
 */
@implementation HybridActionHandler

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- (void)Run:(id)data callback:(ResultHandler)callback {
    NSLog(@"%@, data: %@", self.class, data);
    NSString *actionName = [@"Hybrid" stringByAppendingFormat:@"%@Action", data[@"action"]];//[data[@"action"] stringByAppendingString:@"Action"];
    NSString *methodName = [data[@"method"] stringByAppendingString:@":"];

    Class cls = NSClassFromString(actionName);
    if (!cls){
        callback(NO, @"action class not found.");
        NSLog(@"action class %@ not found.", actionName);
        return;
    }

    HybridAction *action = [cls actionWithWebView:self.webView controller:self.controller];
    [action setResultHandler:callback];
    [action setHybridHandler:self];
    [action actionInitialize];
    SEL sel = NSSelectorFromString(methodName);
    if (action && [action respondsToSelector:sel]){
        [action performSelector:sel withObject:data[@"params"]];
    }else{
        callback(NO, @"action method not found");
        NSLog(@"action method %@/%@ not found.", actionName, methodName);
        return;
    }
}
#pragma clang diagnostic pop

@end
#pragma mark HybridActionHandler


